﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NTL.Assignment;

namespace NTL.AssignmentTest
{
    /// <summary>
    /// Combinational test cases for reader and writer.
    /// </summary>
    [TestClass]
    public class FileReaderWriterBase
    {
        /// <summary>
        /// Initial size of the blocking queue.
        /// </summary>
        protected static int BlockingQueueInitialSize { get; set; }

        /// <summary>
        /// Maximum number of produced items will be held by the queue.
        /// </summary>
        protected static int BlockingQueueMaximumSize { get; set; }

        protected static string TestDirectory = @"D:\NTL_test";

        protected ConcurrentQueue<IList<string>> BlockingQueue = new ConcurrentQueue<IList<string>>();

        protected void ValidateWriterIsPaused()
        {
            ValidateQueueSizeIsConstant();
        }

        protected void ValidateReadersPaused()
        {
            // Wait for pause to take effect.
            Thread.Sleep(TimeSpan.FromSeconds(2));

            // Can fail if async pause call takes more time to take effect.
            ValidateQueueSizeIsConstant();
        }

        protected void ValidateReadersResumed(int previousCount)
        {
            while (previousCount == BlockingQueue.Count)
            {
                if (BlockingQueue.Count == BlockingQueueMaximumSize)
                {
                    break;
                }
            }

            // Moved-on means readers are producing again.
        }

        /// <summary>
        /// Validates whether read operation has begun.
        /// </summary>
        protected void ValidateReaderInAction()
        {
            while (BlockingQueue.Count == 0)
            {
            }

            // Moved-on means readers are producing
        }

        protected void WaitForQueueToBeEmpty()
        {
            while (!BlockingQueue.IsEmpty)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }

            Assert.AreEqual(0, BlockingQueue.Count);
        }

        /// <summary>
        /// Generate fileNames to test.
        /// </summary>
        protected static void GenerateFiles()
        {

            if (Directory.Exists(TestDirectory))
            {
                // Delete existing test directory
                Directory.Delete(TestDirectory, true);
            }

            Directory.CreateDirectory(TestDirectory);

            string parentDirectoryPath = TestDirectory;

            int directories = 20;
            int filesPerDirectory = 10;

            for (int count = 0; count < directories; count++)
            {
                string name = count.ToString();
                string newDirectoryPath = Path.Combine(parentDirectoryPath, name);

                Directory.CreateDirectory(newDirectoryPath);

                for (int i = 0; i < filesPerDirectory; i++)
                {
                    string newFileName = Path.Combine(newDirectoryPath, i.ToString() + ".txt");
                    using (StreamWriter newFile = new StreamWriter(newFileName, false))
                    {
                        newFile.WriteLine(i.ToString());
                    }
                }
            }

            // 20C2
            BlockingQueueMaximumSize = directories * (directories - 1) / 2;
            BlockingQueueMaximumSize = BlockingQueueMaximumSize * 10;
        }

        protected void TestReadWrite(int readers)
        {
            FileReader reader = new FileReader(BlockingQueue, TestDirectory, readers);
            reader.Read();

            string outputFileName = "TestReadWrite-" + readers + ".txt";
            FileWriter writer = new FileWriter(BlockingQueue, outputFileName);
            writer.Write();

            // Clean-up
            reader.Abort();
            writer.Abort();
        }

        protected void TestReadWriteWithPauseResume(int readers)
        {
            FileReader reader = new FileReader(BlockingQueue, TestDirectory, readers);
            reader.Read();
            ValidateReaderInAction();

            FileWriter writer = new FileWriter(BlockingQueue, "TestReadWriteWithPauseResume-" + readers + ".txt");
            writer.Write();

            // Pause the reader\writer and validate.
            writer.Pause();
            reader.Pause();

            // Wait sometime for all threads to get notified.
            Thread.Sleep(TimeSpan.FromSeconds(2));

            ValidateQueueSizeIsConstant();

            int pausedCount = BlockingQueue.Count;

            // Resume readers and validate
            reader.Resume();
            ValidateReadersResumed(pausedCount);

            // Resume the writer.
            writer.Resume();

            WaitForQueueToBeEmpty();

            // Clean-up
            reader.Abort();
            writer.Abort();
        }

        private void ValidateQueueSizeIsConstant()
        {
            int items = BlockingQueue.Count;

            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(items, BlockingQueue.Count);
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }
        }
    }
}
